<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>继承</title>
	</head>
	<body>
		<script type="text/javascript">
			//许多 OO 语言都支持两种继承方式：接口继承和实现继承。接口继承只继承方法签名，而实现继承则继承实际的方法。
			//由于函数没有签名，在 ECMAScript 中无法实现接口继承。
			//ECMAScript 只支持实现继承，而且其实现继承主要是依靠原型链来实现的
			
			//6.3.1 原型链继承
			  //每个构造函数都有一个原型对象(构造函数的prototype属性指向原型对象)
			  //原型对象都包含一个指向构造函数的指针(原型对象的constructor属性指向构造函数)
			  //实例都包含一个指向原型对象的内部指针(实例的内部属性[[Prototype]]指向原型对象，即__proto__指向原型对象)
			  
			  //让原型对象等于另一个类型的实例
			  //此时原型对象包含一个指向另一个原型的指针,即此时原型对象内部属性指向另一个原型对象
				        function Father(){
							this.fatherInfo = 'father';
						}
						Father.prototype.getFatherInfo = function(){
							return this.fatherInfo
						}
						function Son(){
							this.sonInfo = 'son';
						}
						//将构造函数Father的实例赋值给构造函数Son的原型对象，那么Son的原型对象就包含了一个内部指针指向构造函数Father的原型对象
						Son.prototype = new Father();
						Son.prototype.getSonInfo = function(){
							return this.sonInfo
						}
						var son1 = new Son();
						console.log(son1);
						//搜索getFatherInfo方法时，会先现在son1实例中找，结果没有找到，那么会在son1指向的原型对象上查找，son1指向的原型对象相当于一个实例，结果没有找到，
						//那么会在son1指向的原型对象指向的另一个原型对象上查找，结果找到了
						//this对象是在运行时基于函数的执行环境绑定的，getFatherInfo方法中的this对象指向的son1，即son1.fatherInfo
						//此时又会搜索fatherInfo属性，先在son1实例中查找，结果没有找到，那么会在son1指向的原型对象上查找，son1指向的原型对象相当于一个实例，结果没有找到，
						//那么会在son1指向的原型对象指向的另一个原型对象上查找，结果找到了，为'father'
						console.log(son1.getFatherInfo());
                                 
                    //通过实现原型链，本质上扩展了本章前面介绍的原型搜索机制
                    /*以读取模式访问一个实例属性时，首先会在实例中搜索该属性。
                     *如果没有找到该属性，则会继续搜索实例的原型。
                     *在通过原型链实现继承的情况下，搜索过程就得以沿着原型链继续向上。*/  
                     
                 //1.别忘记默认的原型
                        //所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针，指向 Object.prototype。
                             console.log(son1.__proto__=== Son.prototype);//true
							 console.log(Son.prototype.__proto__=== Father.prototype);//true
                             console.log(Father.prototype.__proto__=== Object.prototype)//true
                 //2.确定原型和实例的关系 
                       //可以通过两种方式来确定原型和实例之间的关系。
                       //第一种方式是使用 instanceof 操作符，只要用这个操作符来测试实例与原型链中出现过的构造函数，结果就会返回 true。
                             //由于原型链的关系，可以说son1是Son、Father、Object中任何一个类型的实例
                             console.log(son1 instanceof Son);//true
                             console.log(son1 instanceof Father)//true
                             console.log(son1 instanceof Object)//true
                       //第二种方式是使用 isPrototypeOf()方法。同样，只要是原型链中出现过的原型，都可以说是该原型链所派生的实例的原型，
                       //因此 isPrototypeOf()方法也会返回 true。
                             console.log(Object.prototype.isPrototypeOf(son1));//true
                             console.log(Father.prototype.isPrototypeOf(son1));//true
                             console.log(Son.prototype.isPrototypeOf(son1));//true
                 //3.谨慎地定义方法
                        //子类型有时候需要重写超类型中的某个方法，或者需要添加超类型中不存在的某个方法。
                        //但不管怎样，给原型添加方法的代码一定要放在替换原型的语句之后
                        //在通过原型链实现继承时，不能使用对象字面量创建原型方法。因为这样做就会重写原型链
							function SuperType(){
								this.property = true;
							}
							
							SuperType.prototype.getSuperValue = function(){
								return this.property;
							}
							
							function SubType(){
								this.subproperty = false;
							}
							//继承了SuperType
							SubType.prototype = new SuperType();
							//添加新方法
							SubType.prototype.getSubValue = function(){
								return this.subproperty
							}
							//重写超类型中的方法
							SubType.prototype.getSuperValue = function(){
								return false;
							}
							
							var instance = new SubType();
							var instance2 = new SuperType();
							console.log(instance);
							console.log(instance.getSuperValue());//false
							console.log(instance2);
							console.log(instance2.getSuperValue());//true
                 //4.原型链的问题 
                     //最主要的问题来自包含引用类型值的原型
                     //包含引用类型值的原型属性会被所有实例共享，所以不在原型对象中定义属性
                     //在通过原型来实现继承时，原型实际上会变成另一个类型的实例。于是，原先的实例属性变成了现在的原型属性
                              function Father(){
							  	 this.colors = ["red","blue","green"];//实例属性
							  }
							  function Son(){
							  	
							  }
							  Son.prototype = new Father();//实例属性变成了原型属性，会被共享
							  var son1 = new Son();
							  son1.colors.push("black");
							  console.log(son1.colors);//"red,blue,green,black"
							  var son2  = new Son();
							  console.log(son2.colors);//"red,blue,green,black"
                     //原型链的第二个问题是：在创建子类型的实例时，不能向超类型的构造函数中传递参数。
                     
            //6.3.2 借用构造函数继承
                  	function Father(){
						this.colors = ["red","blue","green"];
					}
					function Son(){
						Father.call(this);
					}
					var son1 = new Son();
					son1.colors.push("black");
					console.log(son1);
					console.log(son1.colors);//"red,blue,green,black"
					var son2 = new Son();
					console.log(son2);
					console.log(son2.colors);//"red,blue,green"
                 //1.传递参数
                     	function Father(name){
							this.name = name;
						}
						function Son(){
							Father.call(this,"ls");
							this.age = 24;
						}
						var son = new Son();
						console.log(son);
						console.log(son.name);//ls
						console.log(son.age);//24
                //2.借用构造函数的问题
                    //无法避免构造函数模式存在的问题——方法都在构造函数中定义，因此函数复用就无从谈起了
                    
            //6.3.3 组合继承
                //apply()和 call()。这两个方法的用途都是在特定的作用域中调用函数，实际上等于设置函数体内 this 对象的值。
                //this对象是在运行时基于函数的执行环境绑定的
                      function Father(name){
					  	 this.name = name;
					  	 this.colors = ["red","blue","green"];
					  }
					  Father.prototype.sayName = function(){
					  	 console.log(this.name);
					  }
					  function Son(name,age){
					  	 //继承属性
					  	 Father.call(this,name)
					  	 this.age = age;
					  }
					  //继承方法
					   Son.prototype = new Father();
					   Son.prototype.constructor = Father;
					   Son.prototype.sayAge = function(){
					   	  console.log(this.age);
					   }
					   var son1 = new Son("ls",24);
					   son1.colors.push("black");
					   console.log( son1);
					   console.log(son1.colors);//"red,blue,green,black"
					   son1.sayName();//"ls"
					   son1.sayAge();//24
					   var son2 = new Son("xxx",25);
					   console.log(son2.colors);//"red,blue,green"
					   son2.sayName();//"xxx"
					   son2.sayAge();//25
		</script>
	</body>
</html>
